﻿using System;
using System.Collections.Generic;
using System.IO.MemoryMappedFiles;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

namespace Sharp.Interprocess
{
    public class CollectionHeader<T>
    {
        #region Private Fields
        private readonly MemoryMappedObject<string> m_CollectionTypeFullNameMMObject;
        private MemoryMappedFile m_PagedMemoryMappedFile;
        private readonly int m_ItemCountOffset;
        private readonly int m_CollectionChangeCountSize;        
        private readonly int m_ItemLastOffsetSize;
        private readonly int m_ItemCountSize;
        private readonly int m_CollectionSizeSize;
        private readonly int m_CollectionOffset;
        private readonly bool m_IsFirstInstance;
        #endregion

        #region Constructor(s)
        public CollectionHeader(int collectionOffset,MemoryMappedFile memoryMappedFile,int collectionMaxSize)
        {
            m_CollectionOffset = collectionOffset;
            m_ItemCountOffset = collectionOffset;
            m_PagedMemoryMappedFile = memoryMappedFile;

            int itemCounterSize = Marshal.SizeOf(typeof(int));
            m_CollectionSizeSize = Marshal.SizeOf(typeof(int));
            m_ItemCountSize = Marshal.SizeOf(typeof(int));
            m_ItemLastOffsetSize = Marshal.SizeOf(typeof(int));
            m_CollectionChangeCountSize = Marshal.SizeOf(typeof(ulong));


            var existingCollectionType = new MemoryMappedObject<string>(memoryMappedFile, m_ItemCountOffset + m_ItemCountSize + m_CollectionChangeCountSize);
            if (existingCollectionType.StoredObject != null && existingCollectionType.StoredObject != typeof(T).FullName)
            {
                m_CollectionTypeFullNameMMObject = existingCollectionType;
            }
            else            
            {
                m_CollectionTypeFullNameMMObject = new MemoryMappedObject<string>(typeof(T).FullName);
            }

            //if collection is newly created
            if (ItemLastOffset == 0)
            {
                m_IsFirstInstance = true;

                //write current collection type
                m_CollectionTypeFullNameMMObject.WriteToMemoryMappedFile(m_PagedMemoryMappedFile, m_ItemCountOffset + m_ItemCountSize + m_CollectionChangeCountSize);
                ItemLastOffset = GetEmptyCollectionItemLastOffset();
                CollectionSize = collectionMaxSize;
            }
            else
            {
                m_IsFirstInstance = false;
            }
        }
        #endregion        

        public void UpdateOnCollectionResize(int newMaxCollectionSize)
        {
            CollectionSize = newMaxCollectionSize;
        }

        public void UpdateOnCollectionChanged()
        {
            CollectionChangeCount++;
        }

        public void UpdateOnItemAdded(int itemStorageSize)
        {
            ItemLastOffset += itemStorageSize;
            ItemCount++;
        }

        public void SetEmptyCollectionValues()
        {
            ItemCount = 0;
            CollectionChangeCount = 0;
            ItemLastOffset = GetEmptyCollectionItemLastOffset();
        }

        public int GetEmptyCollectionItemLastOffset()
        {
            return m_ItemCountOffset + m_ItemCountSize + m_CollectionChangeCountSize + m_CollectionTypeFullNameMMObject.StoredSerializedObjectLength + m_CollectionSizeSize + m_ItemLastOffsetSize;
        }

        public void UpdateMemoryMappedFileReference(MemoryMappedFile memoryMappedFile)
        {
            m_PagedMemoryMappedFile = memoryMappedFile;
        }

        #region Header Properties

        public ulong CollectionChangeCount
        {
            get
            {
                return m_PagedMemoryMappedFile.ReadData<ulong>(m_ItemCountOffset + m_ItemCountSize);
            }
            set
            {
                m_PagedMemoryMappedFile.WriteData(m_ItemCountOffset + m_ItemCountSize, value);
            }
        }

        public Type CollectionType
        {
            get
            {
                return Type.GetType(m_CollectionTypeFullNameMMObject.StoredObject, true);
            }
        }

        public int CollectionSize
        {
            get
            {
                return m_PagedMemoryMappedFile.ReadData<int>(m_ItemCountOffset + m_ItemCountSize + m_CollectionChangeCountSize + m_CollectionTypeFullNameMMObject.StoredSerializedObjectLength);
            }
            set
            {
                m_PagedMemoryMappedFile.WriteData(m_ItemCountOffset + m_ItemCountSize + m_CollectionChangeCountSize + m_CollectionTypeFullNameMMObject.StoredSerializedObjectLength, value);
            }

        }


        public int ItemLastOffset
        {
            get
            {
                return m_PagedMemoryMappedFile.ReadData<int>(m_ItemCountOffset + m_ItemCountSize + m_CollectionChangeCountSize + m_CollectionTypeFullNameMMObject.StoredSerializedObjectLength + m_CollectionSizeSize);
            }
            set
            {
                m_PagedMemoryMappedFile.WriteData(m_ItemCountOffset + m_ItemCountSize + m_CollectionChangeCountSize + m_CollectionTypeFullNameMMObject.StoredSerializedObjectLength + m_CollectionSizeSize, value);
            }

        }

        public int ItemCount
        {
            get
            {
                return m_PagedMemoryMappedFile.ReadData<int>(m_ItemCountOffset);
            }
            set
            {
                m_PagedMemoryMappedFile.WriteData<int>(m_ItemCountOffset, value);
            }
        }

        public bool IsFirstInstance
        {
            get
            {
                return m_IsFirstInstance;
            }
        }
        #endregion

    }
}
